package com.tongtong.middleware.blockchain.controller;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.tongtong.middleware.blockchain.service.ERC1155Service;
import com.tongtong.middleware.blockchain.service.ERC721Service;
import com.tongtong.middleware.blockchain.service.MetadataService;
import com.tongtong.middleware.blockchain.service.TblAssetExtService;
import com.tongtong.middleware.vo.params.MetaDataParams;
import com.tongtong.middleware.vo.params.nft.ApprovalParams;
import com.tongtong.middleware.vo.params.nft.BurnBatchNFTParams;
import com.tongtong.middleware.vo.params.nft.BurnNFTParams;
import com.tongtong.middleware.vo.params.nft.MetaDataByTokenIdParams;
import com.tongtong.middleware.vo.params.nft.MintBatchNFTParams;
import com.tongtong.middleware.vo.params.nft.MintNFTParams;
import com.tongtong.middleware.vo.params.nft.Proposal;
import com.tongtong.middleware.vo.params.nft.TransferBatchParams;
import com.tongtong.middleware.vo.params.nft.TransferParams;
import com.tongtong.middleware.vo.response.SignTxRes;
import com.tongtong.share.client.ShareClient;

import cn.hutool.json.JSONUtil;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.common.response.ApiResult;

/**
 * @author wangqi
 */
@RestController
@RequestMapping("/eth/nft")
@Slf4j
public class NFTController {
	@Autowired
	private ShareClient shareClient;
	@Autowired
	ERC721Service erc721Service;
	@Autowired
	ERC1155Service erc1155Service;
	@Autowired
	TblAssetExtService tblAssetExtService;
	@Autowired
	MetadataService metadataService;

	/**
	 * metadata生成tokenURI
	 */
	@PostMapping("/metadata")
	public ApiResult<String> metadata(@Valid @RequestBody MetaDataParams request, BindingResult bindingResult) {
		log.info("【成功】/eth/nft/metadata request:  {}", JSONUtil.toJsonStr(request));
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		byte[] bytes = JSONUtil.toJsonStr(request).getBytes(StandardCharsets.UTF_8);
		// 将字节数组包装成 ByteArrayInputStream
		InputStream inputStream = new ByteArrayInputStream(bytes);
		MultipartFile multipartFile;
		try {
			multipartFile = new MockMultipartFile("file", "meta.json", "application/json", inputStream);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		// 上传meta信息到oss，获取url
		ApiResult<String> tokenUri = shareClient.upload(multipartFile);
		log.info("【成功】/eth/nft/metadata response: {}", tokenUri);
		return ApiResult.ok(tokenUri.getData());
	}

	/**
	 * 指定TokenId的metadata生成tokenURI
	 */
	@PostMapping("/updateMetadataByTokenId")
	public ApiResult<String> updateMetadataByTokenId(@Valid @RequestBody MetaDataByTokenIdParams request, BindingResult bindingResult) {
		log.info("【成功】/eth/nft/updateMetadataByTokenId request:  {}", JSONUtil.toJsonStr(request));
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		String tokenUri = metadataService.metadataByTokenId(request.getTokenId());
		if (tokenUri.length() < 10) {
			return ApiResult.failure(9527, "Metadata is error");
		}
		log.info("【成功】/eth/nft/metadataByTokenId response: {}", tokenUri);
		return ApiResult.ok(tokenUri);
	}

	/**
	 * 铸造数字资产
	 */
	@PostMapping("/mintNFT")
	public ApiResult<String> mintNFT(@Valid @RequestBody MintNFTParams request, BindingResult bindingResult) {
		log
				.info("【成功】/eth/nft/mintNFT request:  proposal=>{} to=>{} tokenId=>{} amount=>{}", request.getProposal(), request.getTo(), request
						.getTokenId(), request.getAmount());
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		SignTxRes signTxRes = switch (request.getProposal()) {
		case ERC721 -> erc721Service.mintNFT(request.getTo(), request.getTokenId());
		case ERC1155 -> erc1155Service.mintNFT(request.getTo(), request.getTokenId(), request.getAmount());
		};
		if (!signTxRes.getErrMsg().isEmpty()) {
			return ApiResult.failure(9527, signTxRes.getErrMsg());
		} else {
			return ApiResult.ok(signTxRes.getHash());
		}
	}

	/**
	 * 批量铸造数字资产
	 */
	@PostMapping("/mintBatchNFT")
	public ApiResult<String> mintBatchNFT(@Valid @RequestBody MintBatchNFTParams request, BindingResult bindingResult) {
		log
				.info("【成功】/eth/nft/mintBatchNFT request:  proposal=>{} to=>{} ids=>{} values=>{}", request.getProposal(), request.getTo(), JSONUtil
						.toJsonStr(request.getIds()), request.getValues());
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		SignTxRes signTxRes = switch (request.getProposal()) {
		case ERC721 -> new SignTxRes("", "ERC721 not mintBatchNFT");
		case ERC1155 -> erc1155Service.mintBatchNFT(request.getTo(), request.getIds(), request.getValues());
		};
		if (!signTxRes.getErrMsg().isEmpty()) {
			return ApiResult.failure(9527, signTxRes.getErrMsg());
		} else {
			return ApiResult.ok(signTxRes.getHash());
		}
	}

	/**
	 * 转移数字资产
	 */
	@PostMapping("/transfer")
	public ApiResult<String> transfer(@Valid @RequestBody TransferParams request, BindingResult bindingResult) {
		log
				.info("【成功】/eth/nft/transfer request:  proposal=>{} from=>{} to=>{} tokenId=>{} amount=>{}", request.getProposal(), request.getFrom(), request
						.getTo(), request.getTokenId(), request.getAmount());
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		SignTxRes signTxRes = switch (request.getProposal()) {
		case ERC721 -> erc721Service.transfer(request.getFrom(), request.getTo(), request.getTokenId());
		case ERC1155 -> erc1155Service.transfer(request.getFrom(), request.getTo(), request.getTokenId(), request.getAmount());
		};
		if (!signTxRes.getErrMsg().isEmpty()) {
			return ApiResult.failure(9527, signTxRes.getErrMsg());
		} else {
			return ApiResult.ok(signTxRes.getHash());
		}
	}

	/**
	 * 批量转移数字资产
	 */
	@PostMapping("/transferBatch")
	public ApiResult<String> transferBatch(@Valid @RequestBody TransferBatchParams request, BindingResult bindingResult) {
		log
				.info("【成功】/eth/nft/transferBatch request:  proposal=>{} from=>{} to=>{} ids=>{} amounts=>{}", request.getProposal(), request.getFrom(), request
						.getTo(), JSONUtil.toJsonStr(request.getIds()), JSONUtil.toJsonStr(request.getAmounts()));
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		SignTxRes signTxRes = switch (request.getProposal()) {
		case ERC721 -> new SignTxRes("", "ERC721 not transferBatch");
		case ERC1155 -> erc1155Service.transferBatch(request.getFrom(), request.getTo(), request.getIds(), request.getAmounts());
		};
		if (!signTxRes.getErrMsg().isEmpty()) {
			return ApiResult.failure(9527, signTxRes.getErrMsg());
		} else {
			return ApiResult.ok(signTxRes.getHash());
		}
	}

	/**
	 * 授权角色
	 */
	@PostMapping("/approval")
	public ApiResult<String> approval(@Valid @RequestBody ApprovalParams request, BindingResult bindingResult) {
		log.info("【成功】/eth/nft/approval request:  proposal=>{} account=>{}", request.getProposal(), request.getAccount());
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		SignTxRes signTxRes = switch (request.getProposal()) {
		case ERC721 -> erc721Service.approval(request.getAccount());
		case ERC1155 -> erc1155Service.approval(request.getAccount());
		};
		if (!signTxRes.getErrMsg().isEmpty()) {
			return ApiResult.failure(9527, signTxRes.getErrMsg());
		} else {
			return ApiResult.ok(signTxRes.getHash());
		}
	}

	/**
	 * 销毁数字资产
	 */
	@PostMapping("/burnNFT")
	public ApiResult<String> burnNFT(@Valid @RequestBody BurnNFTParams request, BindingResult bindingResult) {
		log
				.info("【成功】/eth/nft/burnNFT request:  proposal=>{} account=>{} tokenId=>{} amount=>{}", request.getProposal(), request.getAccount(), request
						.getTokenId(), request.getAmount());
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		SignTxRes signTxRes = switch (request.getProposal()) {
		case ERC721 -> erc721Service.burnNFT(request.getTokenId());
		case ERC1155 -> erc1155Service.burnNFT(request.getAccount(), request.getTokenId(), request.getAmount());
		};
		if (!signTxRes.getErrMsg().isEmpty()) {
			return ApiResult.failure(9527, signTxRes.getErrMsg());
		} else {
			return ApiResult.ok(signTxRes.getHash());
		}
	}

	/**
	 * 批量销毁数字资产
	 */
	@PostMapping("/burnBatchNFT")
	public ApiResult<String> burnBatchNFT(@Valid @RequestBody BurnBatchNFTParams request, BindingResult bindingResult) {
		log
				.info("【成功】/eth/nft/burnBatchNFT request:  proposal=>{} account=>{} ids=>{} values=>{}", request.getProposal(), request.getAccount(), JSONUtil
						.toJsonStr(request.getIds()), JSONUtil.toJsonStr(request.getValues()));
		if (bindingResult.hasErrors()) {
			return ApiResult.failure(9527, Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
		}
		SignTxRes signTxRes = switch (request.getProposal()) {
		case ERC721 -> new SignTxRes("", "ERC721 not burnBatchNFT");
		case ERC1155 -> erc1155Service.burnBatchNFT(request.getAccount(), request.getIds(), request.getValues());
		};
		if (!signTxRes.getErrMsg().isEmpty()) {
			return ApiResult.failure(9527, signTxRes.getErrMsg());
		} else {
			return ApiResult.ok(signTxRes.getHash());
		}
	}

	/**
	 * 查询指定tokenID的tokenURI
	 */
	@GetMapping("/tokenURI")
	public ApiResult<String> tokenURI(@RequestParam("proposal") Proposal proposal, @RequestParam("tokenId") BigInteger tokenId) {
		return switch (proposal) {
		case ERC721 -> ApiResult.ok(erc721Service.tokenURI(tokenId));
		case ERC1155 -> ApiResult.ok(erc1155Service.uri(tokenId));
		};
	}

	/**
	 * 查询指定账户拥有指定tokenId的数量
	 */
	@GetMapping("/balanceOf")
	public ApiResult<String> balanceOf(@RequestParam("proposal") Proposal proposal, @RequestParam("account") String account,
			@RequestParam("tokenId") BigInteger tokenId) {
		switch (proposal) {
		case ERC721:
			String owner = erc721Service.ownerOf(tokenId);
			if (owner != null && owner.equals(account)) {
				return ApiResult.ok("1");
			} else {
				return ApiResult.ok("0");
			}
		case ERC1155:
			return ApiResult.ok(erc1155Service.balanceOf(account, tokenId));
		default:
			return ApiResult.ok("0");
		}
	}
}
